package cn.aicnn.chatssespringboot.service.Impl;

import cn.aicnn.chatssespringboot.common.CommonResponse;
import cn.aicnn.chatssespringboot.dto.AIAnswerDTO;
import cn.aicnn.chatssespringboot.dto.Chat;
import cn.aicnn.chatssespringboot.dto.ChatRequestDTO;
import cn.aicnn.chatssespringboot.dto.Dialog;
import cn.aicnn.chatssespringboot.persistence.accountMapper;
import cn.aicnn.chatssespringboot.service.*;
import cn.aicnn.chatssespringboot.vo.newQuestionResponseVO;
import cn.aicnn.chatssespringboot.vo.newQuestionVO;
import cn.aicnn.chatssespringboot.vo.streamQuestionResponseVO;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneId;


import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;

@Service
public class GptServiceImpl implements GptService {
    @Autowired
    private accountMapper accountMapper;
    @Autowired
    cn.aicnn.chatssespringboot.persistence.chatMapper chatMapper;
    @Autowired
    cn.aicnn.chatssespringboot.persistence.dialogMapper dialogMapper;
    @Autowired
    private ToolService toolService;
    @Autowired
    private ChatService chatService;
    @Autowired
    private DialogService dialogService;
    @Autowired
    private NewResponseVOService newResponseVOService;
    @Autowired
    private AddressSearchService addressSearchService;
    @Autowired
    private BaiduAIPainting baiduAIPainting;
    //webflux的client
    private WebClient webClient;

    //用于读取第三方的返回结果
    private ObjectMapper objectMapper = new ObjectMapper();

    ArrayList<ChatRequestDTO.ReqMessage> messages = new ArrayList<>();
    @Getter
    private String newMessage = "";

    private int ChatId;

    @PostConstruct
    public void postConstruct() {
        this.webClient = WebClient.builder()//创建webflux的client
                .baseUrl("https://api.openai-proxy.com/v1")//填写对应的api地址
                .defaultHeader("Content-Type", "application/json")//设置默认请求类型
                .build();
    }

    //请求stream的主题
    public Flux<ServerSentEvent<AIAnswerDTO>> doChatGPTStream(int chatId,String requestQuestion) {

        //构建请求对象
        ChatRequestDTO chatRequestDTO = new ChatRequestDTO();
        chatRequestDTO.setModel("gpt-3.5-turbo");//设置模型
        chatRequestDTO.setStream(true);//设置流式返回

        ChatRequestDTO.ReqMessage message = new ChatRequestDTO.ReqMessage();//设置请求消息，在此可以加入自己的prompt
        message.setRole("user");//用户消息
        message.setContent(requestQuestion);//用户请求内容

        messages.add(message);
        chatRequestDTO.setMessages(messages);//设置请求消息

        //构建请求json
        String paramJson = JSONUtil.toJsonStr(chatRequestDTO);

        //使用webClient发送消息
        return this.webClient.post()
                .uri("/chat/completions")//请求uri
                .header("Authorization", "Bearer  sk-proj-JlpPB7kNtpOqqwpBKPbRT3BlbkFJivSmVpyntzCrkSzUtLNh")//设置成自己的key，获得key的方式可以在下文查看
                .header(HttpHeaders.ACCEPT, MediaType.TEXT_EVENT_STREAM_VALUE)//设置流式响应
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(paramJson))
                .retrieve()
                .bodyToFlux(String.class)
                .flatMap(result -> handleWebClientResponse(result));
    }

    public Flux<ServerSentEvent<AIAnswerDTO>> handleWebClientResponse(String resp) {
        if (StrUtil.equals("[DONE]",resp)){//[DONE]是消息结束标识
            addNewMessage();
            return Flux.empty();
        }

        try {
            JsonNode jsonNode = objectMapper.readTree(resp);
            AIAnswerDTO result = objectMapper.treeToValue(jsonNode, AIAnswerDTO.class);//将获得的结果转成对象
            newMessage += result.getChoices().get(0).delta.content;
            System.out.println(newMessage);
            if (CollUtil.size(result.getChoices())  > 0 && !Objects.isNull(result.getChoices().get(0)) &&
                    !StrUtil.isBlank(result.getChoices().get(0).delta.getError())){//判断是否有异常
                throw new RuntimeException(result.getChoices().get(0).delta.getError());
            }
            return Flux.just(result)//返回获得的结果
                    .map(aiAnswerDTO -> ServerSentEvent.<AIAnswerDTO>builder()//进行结果的封装，再返回给前端
                        .data(aiAnswerDTO)
                        .build()
                )
                .onErrorResume(e -> Flux.empty());//发生异常时发送空对象
        } catch (Exception e) {
            return Flux.empty();
        }
    }

    public void addNewMessage(){
        //把newMessage(answer)加入数据库dialog，question为messages里面最后一个对象的content
        ChatRequestDTO.ReqMessage lastone = messages.get(messages.size() - 1);
        dialogService.insertTextDialog(ChatId, lastone.getContent(), newMessage);

        ChatRequestDTO.ReqMessage message = new ChatRequestDTO.ReqMessage();//设置请求消息，在此可以加入自己的prompt
        message.setRole("assistant");//用户消息
        message.setContent(newMessage);//用户请求内容

        messages.add(message);
        newMessage="";
    }
    //当type=1时记录对话到数据库,type=0不记录
    @Override
    public String doChatGPTBlock(String question,int type) {
        //构建请求对象
        ChatRequestDTO chatRequestDTO = new ChatRequestDTO();
        chatRequestDTO.setModel("gpt-3.5-turbo");//设置模型
        chatRequestDTO.setStream(false);

        ChatRequestDTO.ReqMessage message = new ChatRequestDTO.ReqMessage();//设置请求消息
        message.setRole("user");//用户消息
        message.setContent(question);//用户请求内容

        if (type == 1){
            //把问问题的message加到messages里面
            messages.add(message);
            chatRequestDTO.setMessages(messages);//设置请求消息
        }else{
            ArrayList<ChatRequestDTO.ReqMessage> reqMessages = new ArrayList<>();
            reqMessages.add(message);
            chatRequestDTO.setMessages(reqMessages);
        }

        //构建请求json
        String paramJson = JSONUtil.toJsonStr(chatRequestDTO);

        //使用webClient发送消息
        String result = this.webClient.post()
                .uri("/chat/completions")//请求uri
                .header("Authorization", "Bearer sk-proj-JlpPB7kNtpOqqwpBKPbRT3BlbkFJivSmVpyntzCrkSzUtLNh")//设置成自己的key
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(paramJson))
                .retrieve()
                .bodyToMono(String.class)
                .block(); // 阻塞式等待响应
        //打印消息在控制台
        String response = extractAnswerFromJson(result);

        if (type == 1){
            //维护service层的数据，新增了数据库dialog的数据，把question和response（answer）加到数据库
            newMessage = response;
            addNewMessage();
        }

        return response; // 返回接收到的消息
    }

    public String extractAnswerFromJson(String json) {
        try {
            // 使用Jackson ObjectMapper解析JSON字符串
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = objectMapper.readTree(json);

            // 提取回答部分
            String answer = jsonNode.get("choices").get(0).get("message").get("content").asText();


            for (ChatRequestDTO.ReqMessage message1 : messages){
                System.out.println("role:"+message1.getRole());
                System.out.println("content:"+message1.getContent());
            }
            return answer;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    //新建问题所需要的接口
    @Override
    public Object newQuestion(newQuestionVO request) throws IOException, InterruptedException {
        //获取请求体中的内容
        String userid = request.getUsername();
        String question = request.getQuestion();
        boolean typeOfAnswer = request.isType();

        //自动设置chatId并新建一个chat
        int chatId = chatService.insertChat(question, userid);
        ChatId = chatId;

        //判断问题的类型
        String prompt = "你是一个文本分类器,请你仔细理解并按照要求将"+question+"这个问题分类到：[天气查询，绘画任务，其他任务]，输出格式：[0,1,2]。" +
                "如果判断类别为天气查询，请按以下标准输出：参数：0。" +
                "如果判断类别为绘画任务，请按以下标准输出：参数：1。" +
                "如果判断类别为其他任务，请按以下标准输出：参数：2。" ;
        String categoryString = doChatGPTBlock(prompt,0);
        char categoryChar = categoryString.charAt(categoryString.length() - 1);
        int category = Character.getNumericValue(categoryChar);

        System.out.println("categoryString"+categoryString);
        if(category == 0){
            //天气查询
            return askByWeather(question);
        } else if (category ==1) {
            //绘画任务时
            return aiPainting(question);
        }else {
            //正常对话
            return doChatGPTBlock(question,chatId);
        }
    }
    //问问题的接口
    @Override
    public Object ask(newQuestionVO request, int chatid) throws IOException, InterruptedException {


        ChatId = chatid;
        //获取请求体中的内容
        String userid = request.getUsername();
        String question = request.getQuestion();

        String prompt = "你是一个文本分类器,请你仔细理解并按照要求将"+question+"这个问题分类到：[天气查询，绘画任务，其他任务]，输出格式：[0,1,2]。" +
                "如果判断类别为天气查询，请按以下标准输出：参数：0。" +
                "如果判断类别为绘画任务，请按以下标准输出：参数：1。" +
                "如果判断类别为其他任务，请按以下标准输出：参数：2。" ;
        String categoryString = doChatGPTBlock(prompt,0);
        char categoryChar = categoryString.charAt(categoryString.length() - 1);
        int category = Character.getNumericValue(categoryChar);
        if(category == 0){
            //天气查询
            return askByWeather(question);
        } else if (category ==1) {
            //绘画任务时
            return aiPainting(question);
        }else {
            //正常对话
            return doChatGPTBlock(question,chatid);
        }
    }
    //用流式传输
    @Override
    public Object askByStream(int chatId, String question){
        try{
            ChatId = chatId;
            String prompt = "你是一个文本分类器,请你仔细理解并按照要求将"+question+"这个问题分类到：[天气查询，绘画任务，其他任务]，输出格式：[0,1,2]。" +
                    "如果判断类别为天气查询，请按以下标准输出：参数：0。" +
                    "如果判断类别为绘画任务，请按以下标准输出：参数：1。" +
                    "如果判断类别为其他任务，请按以下标准输出：参数：2。" ;
            String categoryString = doChatGPTBlock(prompt,0);
            char categoryChar = categoryString.charAt(categoryString.length() - 1);
            int category = Character.getNumericValue(categoryChar);
            if(category == 0){
                //天气查询
                return askByWeather(question);
            } else if (category ==1) {
                //绘画任务时
                return aiPainting(question);
            }else {
                //正常对话
                return doChatGPTStream(chatId,question);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return "ERROR";
    }

    @Override
    public Object newQuestionByStream(String userid, String question) throws IOException, InterruptedException {
        System.out.println("username:"+userid);
        //自动设置chatId并新建一个chat
        int chatId = chatService.insertChat(question, userid);
        ChatId = chatId;

        //判断问题的类型
        String prompt = "你是一个文本分类器,请你仔细理解并按照要求将"+question+"这个问题分类到：[天气查询，绘画任务，其他任务]，输出格式：[0,1,2]。" +
                "如果判断类别为天气查询，请按以下标准输出：参数：0。" +
                "如果判断类别为绘画任务，请按以下标准输出：参数：1。" +
                "如果判断类别为其他任务，请按以下标准输出：参数：2。" ;
        String categoryString = doChatGPTBlock(prompt,0);
        char categoryChar = categoryString.charAt(categoryString.length() - 1);
        int category = Character.getNumericValue(categoryChar);

        System.out.println("categoryString"+categoryString);
        if(category == 0){
            //天气查询
            return askByWeather(question);
        } else if (category ==1) {
            //绘画任务时
            return aiPainting(question);
        }else {
            //正常对话
            return doChatGPTStream(chatId,question);
        }
    }

    //天气查询的逻辑
    @Override
    public Object askByWeather(String question) {
        try{
            //prompt使用chatGPT解析自然语言中的隐含信息
            String prompt1 = "请帮我把'"+question+"'这个问题分析出里面的时间，地点以及需要获取的天气内容是简略的，详细的还是未来的几天的,地点的输出内容为无，当前地点，城市，省份;类型的输出内容为[简略，详细，未来]，这个回答的输出格式为:时间/地点/类型，示例：今天/北京/详细";

            //获取使用chatGPT解析出来的信息数据
            String parameter = doChatGPTBlock(prompt1,0);
            String [] parameters = parameter.split("/");
            String time = parameters[0];
            String location = parameters[1];
            String type = parameters[2];
            System.out.println(parameter);

            String answer1 = "";
            if (location.equals("无") ||location.equals("当前地点"))
                location = addressSearchService.getAddressByIP("120.227.56.17");
            if (!time.equals("今天")){
                answer1 = toolService.getWeatherAndFuture(location);
            }else{
                if (type.equals("简略"))
                    answer1 = toolService.getWeatherAndNow(location);
                else
                    answer1 = toolService.getWeatherAnd24(location);
            }

            //question1作为question，answer1作为answer放入数据库dialog
            dialogService.insertTextDialog(ChatId, question, answer1);

            newQuestionResponseVO newQuestionResponseVO = newResponseVOService.createTextResponse(0, question, answer1);
            CommonResponse<newQuestionResponseVO> commonResponse = CommonResponse.createForSuccess("SUCCESS", newQuestionResponseVO);
            // 使用 ObjectMapper 将 CommonResponse 对象序列化为 JSON 格式
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(commonResponse);
            // 返回 JSON 格式的数据
            return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(commonResponse);
        }catch (Exception e){
            //当出错的时候自动调用正常对话
            return doChatGPTBlock(question,0);
        }
    }

    @Override
    public String aiPainting(String promot) throws IOException, InterruptedException {
        String taskId = baiduAIPainting.submitRequest(promot);
        System.out.println("Task ID: " + taskId);

        List<String> imgUrls = baiduAIPainting.queryResult(taskId);
        String savedImagePath = baiduAIPainting.downloadAndSaveImage(imgUrls, "E:\\java\\aitool-back\\img");
        System.out.println("Image saved at: " + savedImagePath);

        Dialog dialog = new Dialog();
        dialog.setChatid(ChatId);
        dialog.setQuestion(promot);
        dialog.setAnswer("");
        dialog.setType(true);
        dialog.setImg(savedImagePath);
        dialogMapper.insert(dialog);
        return savedImagePath;
    }
}